package com.fbs.wowo.chat.service.imp;

import com.fbs.wowo.chat.entity.Crew;
import com.fbs.wowo.chat.entity.CrewMember;
import com.fbs.wowo.chat.mapper.CrewMapper;
import com.fbs.wowo.chat.mapper.CrewMemberMapper;
import com.fbs.wowo.chat.service.CrewService;
import com.fbs.wowo.chat.vo.CrewMemberVo;
import com.fbs.wowo.exception.ErrorCode;
import com.fbs.wowo.exception.GivenNullParamException;
import com.fbs.wowo.exception.InputVerifyFailedException;
import com.fbs.wowo.exception.NoSuchDataException;
import com.fbs.wowo.manage.entity.User;
import com.fbs.wowo.manage.mapper.UserMapper;
import com.fbs.wowo.util.common.Generator;
import com.fbs.wowo.util.rongcloud.RCMessage;
import com.fbs.wowo.util.rongcloud.RCUtils;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

// TODO 自己建的群列表
// TODO 照片
// TODO 删除好友
// TODO 上传头像
// TODO 群二维码拼接字符串？
// TODO 打印一下访问请求

// （获取融云给的参数，或从数据库取值，判断当前群组人数)
@Service("crewService")
@Transactional
public class CrewServiceImp implements CrewService {

    /**
     * 创建群
     * @param crew 待创建的群组
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public Crew create(Crew crew) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        if (crew == null || !StringUtils.hasText(crew.getName())) throw new GivenNullParamException();

        // 创建群组
        createCrew(crew);
        return crew;
    }

    /**
     * 创建群并邀请好友入群
     * @param crew 待创建的群
     * @param uids 用户id
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public Crew create(Crew crew, String[] uids) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        if (crew == null || !StringUtils.hasText(crew.getUid()) || !StringUtils.hasText(crew.getName()))
            throw new GivenNullParamException();
        // 创建群组
        createCrew(crew);
        // 各好友 进 用户群组中间表 已经进群的就不再进了
        crewAddUsers(uids, crew);
        return crew;
    }

    /**
     * 邀请好友入群 忽略异常(已入群的不报错)
     * @param crewId 群组id
     * @param uids 用户id数组
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public void invite(String crewId, String[] uids) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        Crew crew = validateCrew(crewId);
        // TODO 如果自己不在群里不能添加自己入群
        crewAddUsers(uids, crew);
        // TODO 加入成功时，给该用户发送加入成功的消息
    }

    /**
     * 通过加群验证
     *
     * crewId uuid id
     * @param crewId 群组id
     * @param userId 要加群的用户id
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public void join(String crewId, String userId) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        Crew crew = validateCrew(crewId);
        User u = validateUser(userId);
        validateExistedCrewMember(userId, crewId);

        joinCrew(u, crew);
        // TODO 加入成功时，给该用户发送加入成功的消息
    }

    /**
     * 用户申请加群
     *
     * @param crewId 群组id
     * @param userId 要加群的用户id
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public Crew reply(String crewId, String userId, String content) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        Crew crew = validateCrew(crewId);
        User u = validateUser(userId);
        // 不能邀请自己入群，不能邀请已在群里的好友入群
        validateExistedCrewMember(userId, crewId);
        // 假如不需要验证则直接插入 valid 1
        // ，否则 valid 0，融云 给群主发验证消息
        if (crew.getPub() == 1) {
            // 不需要验证
            joinCrew(u, crew);
            // TODO 给用户发送加好友成功的消息
            return crew;
        } else {
            // 需要验证，给群主发消息
            // 融云
            RCMessage rcMsg = new RCMessage(userId, crew.getUid(), u.getName(), 1, crewId, crew.getName(), content, RCUtils.REPLY_GROUP);
            RCUtils.invite(rcMsg);
            return null;
        }
    }

    /**
     * 用户加入的群组列表
     *
     * @param userId 用户id
     * @return 用户加入的群组列表
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public List<Crew> crewList(String userId) throws GivenNullParamException, NoSuchDataException {
        validateUser(userId);
        return crewMapper.crewList(userId);
    }

    /**
     * 群成员列表
     *
     * @param crewId 群组id
     * @return 成员列表（带头像）
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public List<CrewMemberVo> crewMember(String crewId) throws GivenNullParamException, NoSuchDataException {
        validateCrew(crewId);
        return crewMemberMapper.crewMemberList(crewId);
    }

    /**
     * 用户退出群&被踢出群
     * 只有群主和本人有这个权利操作
     * 群主不能被踢
     * 删除时更新群人数
     *
     * @param crewId 群组id
     * @param userId 要退群的用户id
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public void remove(String crewId, String userId) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        Crew crew = validateCrew(crewId);
        validateUser(userId);
        if (crew.getUid().equals(userId)) throw new InputVerifyFailedException(ErrorCode.ERR_OWNER_CANNOT_QUIT);

        // 验证用户是否在群里（中间表）
        CrewMember cm = findCrewMemberValid(crewId, userId);
        if (cm == null) throw new NoSuchDataException(ErrorCode.ERR_NOT_IN_GROUP);
        // 融云
        RCUtils.quitCrew(crewId, userId);
        // 更新群组人数
        crew.setNum(RCUtils.crewSize(crewId));
        modifyCrew(crew);
        // 删除中间表
        cm.setValid((byte) 0);
        modifyCrewMember(cm);
    }

    /**
     * 解散群
     *
     * @param crewId 群组id
     * @param ownerId 群主id
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public void del(String crewId, String ownerId) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        Crew crew = validateCrew(crewId);
        validateUser(ownerId);
        if (!crew.getUid().equals(ownerId)) throw new InputVerifyFailedException(ErrorCode.ERR_ONLY_OWNER_CAN_DO);
        // 融云
        RCUtils.removeCrew(crewId, ownerId);
        // 从数据库删除相关联的crewMember和crew记录
        // 取消群的有效性
        crew.setValid((byte) 0);
        // 取消crewMember所有群员的验证
        crewMemberMapper.removeCrew(crewId);
        modifyCrew(crew);
    }

    /**
     * 查找群
     *  查看群信息
     * 是否在群
     * 是否屏蔽
     * @param crewId 群组id
     * @return 数据库查找到的群组数据
     */
    @Override
    public Crew find(String crewId, String userId) throws NoSuchDataException {
        Crew crew = findByCid(crewId);
        if (crew == null) throw new NoSuchDataException(ErrorCode.ERR_GROUP_NOT_EXIST);
        CrewMember crewMember = findCrewMemberValid(crewId, userId);
        if (crewMember == null) {
            crew.setIsCrewMember(0);
            crew.setShield(0);
            crew.setNickname("");
        } else {
            crew.setIsCrewMember(1);
            crew.setShield(crewMember.getShield().intValue());
            crew.setNickname(crewMember.getNickname());
        }

        return crew;
    }

    /**
     * 修改群信息
     * TODO 头像
     * @param crew 群组
     * @param ownerId 群主id
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    @Override
    public Crew modifyCrew(Crew crew, String ownerId) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        if (crew == null) throw new GivenNullParamException();
        Crew crewDb = validateCrew(crew.getCid());
        validateUser(ownerId);

        if (!crewDb.getUid().equals(ownerId)) throw new InputVerifyFailedException(ErrorCode.ERR_ONLY_OWNER_CAN_DO);

        // 融云
        // 名称改变
        if (StringUtils.hasText(crew.getName()) && !crew.getName().equals(crewDb.getName())) {
            RCUtils.crewUpdate(crew.getCid(), crew.getName());
        }
        crew.setId(crewDb.getId());
        crew.setUpdateTime(new Date());
        modifyCrew(crew);

        return crew;
    }

    /**
     * 修改群名片
     *
     * @param crewId 用户id
     * @param userId 融云id
     */
    @Override
    public CrewMember modifyCrewNickname(String crewId, String userId, String nickname) {
        if (!StringUtils.hasText(nickname)) throw new GivenNullParamException(ErrorCode.ERR_NULL_PARAM);
        CrewMember crewMember = findCrewMemberValid(crewId, userId);
        if (crewMember == null) throw new NoSuchDataException(ErrorCode.ERR_NOT_IN_GROUP);
        crewMember.setNickname(nickname);
        modifyCrewMember(crewMember);
        return crewMember;
    }

    /**
     * 是否屏蔽群
     * @return 0不屏蔽 1屏蔽
     */
    @Override
    public Integer shield(String crewId, String userId, Byte shield) {
        validateUser(userId);
        CrewMember crewMember = findCrewMemberValid(crewId, userId);
        crewMember.setShield(shield);
        modifyCrewMember(crewMember);

        return crewMember.getShield().intValue();
    }

    /**
     * 模糊查询群组
     *
     * @param query 模糊查询字符串
     */
    @Override
    public List<Crew> findByQueryString(String query) {
        // TODO 各种非空判断
        // 按名字查
        List<Crew> searchByName = crewMapper.searchByName(query);
        List<Crew> searchById = crewMapper.searchById(query);

        List<Crew> list = new ArrayList<>();

        if (searchById != null) {
            list.addAll(searchById);
        }
        if (searchByName != null) {
            list.addAll(searchByName);
        }
        // 按群号查
        return list;
    }

    /**
     * 热门群组
     * @return 群组列表
     */
    @Override
    public List<Crew> hotCrew() {
        // 只取前10
        PageHelper.startPage(1, 10);
        return crewMapper.findHot();
    }


    /**************************************
     *
     *
     * 私有方法
     *
     *
     **************************************/

    /**
     * 管理员创建群组
     * @param crew 被创建的群组
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    private String createCrew(Crew crew) throws GivenNullParamException, NoSuchDataException {
        User user = validateUser(crew.getUid());
        if (!StringUtils.hasText(crew.getName())) throw new GivenNullParamException(ErrorCode.ERR_GROUP_NO_NAME);
        crew.setCid(Generator.generateId());
        // TODO 群头像

        // TODO 增加用户建群数量
        // 融云
        RCUtils.regCrew(crew);
        // RCUtils.crewSize(crew.getCid())
        insertCrew(crew);
        // 添加群主 进 用户群组中间表
        createCrewMember(crew.getCid(), user.getUserId(), user.getName());

        return crew.getCid();
    }

    /**
     * 群组加人（融云 + 数据库）
     * @param uids 要加群的用户id
     * @param crew 被加的群组
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    private void crewAddUsers(String[] uids, Crew crew) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        // 融云：邀请入群
        RCUtils.inviteCrew(crew.getCid(), crew.getName(), uids);
        // 直接加入（插入中间表）
        Arrays.asList(uids).forEach(uid -> {
            User u = validateUser(uid);
            createCrewMember(crew.getCid(), u.getUserId(), u.getName());
        });
        // 更新群人数
        updateCrewNum(crew);
    }

    /**
     * 申请加入群组 融云调用方法有别于邀请入群
     * @param user 将数据库取到的用户的id和名称传入创建群众
     * @param crew  需要cid进行操作和crew更新群员数量
     * @throws GivenNullParamException 参数为空
     * @throws NoSuchDataException 数据库没有数据
     * @throws InputVerifyFailedException 输入数据异常
     */
    private void joinCrew(User user, Crew crew) throws GivenNullParamException, NoSuchDataException, InputVerifyFailedException {
        // 融云
        RCUtils.joinCrew(crew.getCid(), user.getUserId(), crew.getName());
        // 直接加入（插入中间表）
        createCrewMember(crew.getCid(), user.getUserId(), user.getName());
        // 更新群人数
        updateCrewNum(crew);
    }

    /**
     * 更新群组人数
     * @param crew 必须是从数据库拿到的crew实体
     */
    private void updateCrewNum(Crew crew) {
        crew.setNum(RCUtils.crewSize(crew.getCid()));
        // 设置除id外其他项为null？
        modifyCrew(crew);
    }

    /**
     * 向数据库插入用户群组中间表记录
     * @param cid 群组id
     * @param uid 用户id
     */
    private void createCrewMember(String cid, String uid, String name) {
        CrewMember cm = findCrewMember(cid, uid);
        // 已经在群里了的就不往数据库添加了
        if (cm != null) {
            // 被踢出去的加进来
            if (cm.getValid() == (byte) 0) {
                cm.setValid((byte) 1);
                modifyCrewMember(cm);
            }
            return;
        }

        CrewMember crewMember = new CrewMember();
        crewMember.setCid(cid);
        crewMember.setUid(uid);
        crewMember.setCreateTime(new Date());
        crewMember.setValid((byte) 1);
        crewMember.setShield((byte) 0);
        crewMember.setName(name);
        saveCrewMember(crewMember);
    }

    /**
     * 创建群之前要确认融云上的群组人数，设置到数据库num字段
     * 并且设置一些默认值
     * @param crew 群组信息
     */
    private void insertCrew(Crew crew) {
//        crew.setNum(1);
        crew.setCreateTime(new Date());
        if (crew.getPub() == null) crew.setPub((byte) 1);
        crew.setValid((byte) 1);
        crew.setNum(RCUtils.crewSize(crew.getCid()));
        crewMapper.insertSelective(crew);
    }

    /**
     * 验证要加群的用户是否已经在群里
     * 不能邀请自己入群，不能邀请已在群里的好友入群
     * 已经入群退出群再进的需要同意
     * @param userId 用户id
     * @param crewId 群id
     */
    private CrewMember validateExistedCrewMember(String userId, String crewId) throws InputVerifyFailedException {
        CrewMember crewMember = findCrewMember(crewId, userId);
        if (crewMember != null && crewMember.getValid() == (byte) 1) {
            throw new InputVerifyFailedException(ErrorCode.ERR_HAS_JOIN_GROUP);
        }
        return crewMember;
    }

    /**
     * 验证群组id
     * @param crewId 群id
     * @return 数据库返回的群信息
     * @throws GivenNullParamException 空参数
     */
    private Crew validateCrew(String crewId) throws GivenNullParamException, NoSuchDataException {
        if (!StringUtils.hasText(crewId)) throw new GivenNullParamException();
        Crew crew = findByCid(crewId);
        if (crew == null) throw new NoSuchDataException(ErrorCode.ERR_GROUP_NOT_EXIST);
        return crew;
    }

    /**
     * 验证用户userId
     * @param userId 用户userId
     * @return 数据库返回的用户
     * @throws GivenNullParamException 空参数
     * @throws NoSuchDataException 数据库没数据
     */
    private User validateUser(String userId) throws GivenNullParamException, NoSuchDataException {
        if (!StringUtils.hasText(userId)) throw new GivenNullParamException();
        User u = userMapper.findByUserId(userId);
        if (u == null) throw new NoSuchDataException(ErrorCode.ERR_USER_NOT_EXIST);
        return u;
    }

    /*************************************************
     *
     * 底层实现
     *
     *************************************************/


    private int modifyCrewMember(CrewMember cm) {
        return crewMemberMapper.updateByPrimaryKeySelective(cm);
    }

    private void saveCrewMember(CrewMember crewMember) {
        crewMemberMapper.insertSelective(crewMember);
    }

    private void modifyCrew(Crew crew) {
        crewMapper.updateByPrimaryKeySelective(crew);
    }
    /**
     * 按群组id查找群组
     * @param crewId 群组id
     */
    private Crew findByCid(String crewId) {
        return crewMapper.findByCid(crewId);
    }

    /**
     * 查找群成员
     * @param crewId 群组id
     * @param userId 用户id
     * @return 群组成员
     */
    private CrewMember findCrewMember(String crewId, String userId) {
        // TODO str null
        return crewMemberMapper.selectByPrimaryKey(userId, crewId);
    }
    /**
     * 查找群成员
     * @param crewId 群组id
     * @param userId 用户id
     * @return 群组成员
     */
    private CrewMember findCrewMemberValid(String crewId, String userId) {
        // TODO str null
        return crewMemberMapper.selectByPrimaryKeyValid(userId, crewId);
    }


    @Autowired
    private CrewMapper crewMapper;
    @Autowired
    private CrewMemberMapper crewMemberMapper;
    @Autowired
    private UserMapper userMapper;
}
